Merge pull request #160 from scavone/master

Added ForecastIO agent

Andrew Cantino 10 years ago
parent
commit
49be934792
3 changed files with 123 additions and 20 deletions
  1. 1 0
      Gemfile
  2. 5 0
      Gemfile.lock
  3. 117 20
      app/models/agents/weather_agent.rb

+ 1 - 0
Gemfile

@@ -32,6 +32,7 @@ gem 'kramdown'
32 32
 gem "typhoeus"
33 33
 gem 'nokogiri'
34 34
 gem 'wunderground'
35
+gem 'forecast_io'
35 36
 gem 'rturk'
36 37
 
37 38
 gem "twitter", '~> 5.7.1'

+ 5 - 0
Gemfile.lock

@@ -109,6 +109,10 @@ GEM
109 109
       multipart-post (>= 1.2, < 3)
110 110
     fastercsv (1.5.5)
111 111
     ffi (1.9.0)
112
+    forecast_io (2.0.0)
113
+      faraday
114
+      hashie
115
+      multi_json
112 116
     foreman (0.63.0)
113 117
       dotenv (>= 0.7)
114 118
       thor (>= 0.13.6)
@@ -316,6 +320,7 @@ DEPENDENCIES
316 320
   dotenv-rails
317 321
   em-http-request
318 322
   fastercsv
323
+  forecast_io
319 324
   foreman
320 325
   geokit-rails3
321 326
   jquery-rails

+ 117 - 20
app/models/agents/weather_agent.rb

@@ -6,18 +6,26 @@ module Agents
6 6
     cannot_receive_events!
7 7
 
8 8
     description <<-MD
9
-      The WeatherAgent creates an event for the following day's weather at a given `location`.
9
+      The WeatherAgent creates an event for the day's weather at a given `location`.
10 10
 
11
-      The `location` can be a US zipcode, or any location that Wunderground supports.  To find one, search [wunderground.com](http://wunderground.com) and copy the location part of the URL.  For example, a result for San Francisco gives `http://www.wunderground.com/US/CA/San_Francisco.html` and London, England gives `http://www.wunderground.com/q/zmw:00000.1.03772`.  The locations in each are `US/CA/San_Francisco` and `zmw:00000.1.03772`, respectively.
11
+      You also must select `which_day` you would like to get the weather for where the number 0 is for today and 1 is for tomorrow and so on. Weather is only returned for 1 week at a time.
12 12
 
13
-      You must setup an [API key for Wunderground](http://www.wunderground.com/weather/api/) in order to use this Agent.
13
+      The weather can be provided by either Wunderground or ForecastIO. To choose which `service` to use, enter either `forecastio` or `wunderground`.
14
+
15
+      The `location` can be a US zipcode, or any location that Wunderground supports. To find one, search [wunderground.com](http://wunderground.com) and copy the location part of the URL.  For example, a result for San Francisco gives `http://www.wunderground.com/US/CA/San_Francisco.html` and London, England gives `http://www.wunderground.com/q/zmw:00000.1.03772`.  The locations in each are `US/CA/San_Francisco` and `zmw:00000.1.03772`, respectively.
16
+
17
+      If you plan on using ForecastIO, the `location` must be a set of GPS coordinates.
18
+
19
+      You must setup an [API key for Wunderground](http://www.wunderground.com/weather/api/) in order to use this Agent with Wunderground.
20
+
21
+      You must setup an [API key for Forecast](https://developer.forecast.io/) in order to use this Agent with ForecastIO.
14 22
     MD
15 23
 
16 24
     event_description <<-MD
17 25
       Events look like this:
18 26
 
19 27
           {
20
-            "location": 12345,
28
+            "location": "12345",
21 29
             "date": {
22 30
               "epoch": "1357959600",
23 31
               "pretty": "10:00 PM EST on January 11, 2013"
@@ -44,38 +52,127 @@ module Agents
44 52
       event_created_within?(2) && !recent_error_logs?
45 53
     end
46 54
 
47
-    def wunderground
48
-      Wunderground.new(options['api_key']) if key_setup?
49
-    end
50
-
51 55
     def key_setup?
52
-      options['api_key'] && options['api_key'] != "your-key"
56
+      options['api_key'].present? && options['api_key'] != "your-key"
53 57
     end
54 58
 
55 59
     def default_options
56 60
       {
57
-        'api_key' => "your-key",
58
-        'location' => "94103"
61
+        'service' => 'wunderground',
62
+        'api_key' => 'your-key',
63
+        'location' => '94103',
64
+        'which_day' => '1'
59 65
       }
60 66
     end
61 67
 
68
+    def service
69
+      options["service"].presence || "wunderground"
70
+    end
71
+
72
+    def which_day
73
+      (options["which_day"].presence || 1).to_i
74
+    end
75
+
76
+    def location
77
+      options["location"].presence || options["zipcode"]
78
+    end
79
+
62 80
     def validate_options
63
-      errors.add(:base, "location is required") unless options['location'].present? || options['zipcode'].present?
64
-      errors.add(:base, "api_key is required") unless options['api_key'].present?
81
+      errors.add(:base, "service is required") unless service.present?
82
+      errors.add(:base, "service must be set to 'forecastio' or 'wunderground'") unless ["forecastio", "wunderground"].include?(service)
83
+      errors.add(:base, "location is required") unless location.present?
84
+      errors.add(:base, "api_key is required") unless key_setup?
85
+      errors.add(:base, "which_day selection is required") unless which_day.present?
65 86
     end
66 87
 
67
-    def check
88
+    def wunderground
89
+      Wunderground.new(options['api_key']).forecast_for(location)['forecast']['simpleforecast']['forecastday'] if key_setup?
90
+    end
91
+
92
+    def forecastio
68 93
       if key_setup?
69
-        wunderground.forecast_for(options['location'] || options['zipcode'])["forecast"]["simpleforecast"]["forecastday"].each do |day|
70
-          if is_tomorrow?(day)
71
-            create_event :payload => day.merge('location' => options['location'] || options['zipcode'])
72
-          end
94
+        ForecastIO.api_key = options['api_key']
95
+        lat, lng = location.split(',')
96
+        ForecastIO.forecast(lat,lng)['daily']['data']
97
+      end
98
+    end
99
+
100
+    def model(service,which_day)
101
+      if service == "wunderground"
102
+        wunderground[which_day]
103
+      elsif service == "forecastio"
104
+        forecastio.each do |value|
105
+          timestamp = Time.at(value.time)
106
+          if (timestamp.to_date - Time.now.to_date).to_i == which_day
107
+            day = {
108
+              'date' => {
109
+                'epoch' => value.time.to_s,
110
+                'pretty' => timestamp.strftime("%l:%M %p %Z on %B %d, %Y"),
111
+                'day' => timestamp.day,
112
+                'month' => timestamp.month,
113
+                'year' => timestamp.year,
114
+                'yday' => timestamp.yday,
115
+                'hour' => timestamp.hour,
116
+                'min' => timestamp.strftime("%M"),
117
+                'sec' => timestamp.sec,
118
+                'isdst' => timestamp.isdst ? 1 : 0 ,
119
+                'monthname' => timestamp.strftime("%B"),
120
+                'monthname_short' => timestamp.strftime("%b"),
121
+                'weekday_short' => timestamp.strftime("%a"),
122
+                'weekday' => timestamp.strftime("%A"),
123
+                'ampm' => timestamp.strftime("%p"),
124
+                'tz_short' => timestamp.zone
125
+              },
126
+              'period' => which_day.to_i,
127
+              'high' => {
128
+                'fahrenheit' => value.temperatureMax.round().to_s,
129
+                'epoch' => value.temperatureMaxTime.to_s,
130
+                'fahrenheit_apparent' => value.apparentTemperatureMax.round().to_s,
131
+                'epoch_apparent' => value.apparentTemperatureMaxTime.to_s,
132
+                'celsius' => ((5*(Float(value.temperatureMax) - 32))/9).round().to_s
133
+              },
134
+              'low' => {
135
+                'fahrenheit' => value.temperatureMin.round().to_s,
136
+                'epoch' => value.temperatureMinTime.to_s,
137
+                'fahrenheit_apparent' => value.apparentTemperatureMin.round().to_s,
138
+                'epoch_apparent' => value.apparentTemperatureMinTime.to_s,
139
+                'celsius' => ((5*(Float(value.temperatureMin) - 32))/9).round().to_s
140
+              },
141
+              'conditions' => value.summary,
142
+              'icon' => value.icon,
143
+              'avehumidity' => (value.humidity * 100).to_i,
144
+              'sunriseTime' => value.sunriseTime.to_s,
145
+              'sunsetTime' => value.sunsetTime.to_s,
146
+              'moonPhase' => value.moonPhase.to_s,
147
+              'precip' => {
148
+                'intensity' => value.precipIntensity.to_s,
149
+                'intensity_max' => value.precipIntensityMax.to_s,
150
+                'intensity_max_epoch' => value.precipIntensityMaxTime.to_s,
151
+                'probability' => value.precipProbability.to_s,
152
+                'type' => value.precipType
153
+              },
154
+              'dewPoint' => value.dewPoint.to_s,
155
+              'avewind' => {
156
+                'mph' => value.windSpeed.round().to_s,
157
+                'kph' =>  (Float(value.windSpeed) * 1.609344).round().to_s,
158
+                'degrees' => value.windBearing.to_s
159
+              },
160
+              'visibility' => value.visibility.to_s,
161
+              'cloudCover' => value.cloudCover.to_s,
162
+              'pressure' => value.pressure.to_s,
163
+              'ozone' => value.ozone.to_s
164
+            }
165
+            return day
166
+          end    
73 167
         end
74 168
       end
75 169
     end
76 170
 
77
-    def is_tomorrow?(day)
78
-      Time.zone.at(day["date"]["epoch"].to_i).to_date == Time.zone.now.tomorrow.to_date
171
+    def check
172
+      if key_setup?
173
+        create_event :payload => model(service, which_day).merge('location' => location)
174
+      end
79 175
     end
176
+
80 177
   end
81 178
 end